package com.med.service.impl;

import com.med.dto.RespondDTO;
import com.med.entity.AppointmentDO;
import com.med.entity.DepartmentDO;
import com.med.entity.DoctorDO;
import com.med.mapper.AppointmentMapper;
import com.med.openfeign.IAppointmentFeign;
import com.med.service.IAppointmentService;
import com.med.util.TreeMenuUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: internet-based-healthcare
 * @BelongsPackage: com.med.service.impl
 * @Author: 小吴
 * @CreateTime: 2025-08-21 16:29
 * @Description: TODO
 * @Version: 1.0
 */
@Service
public class AppointmentServiceImpl implements IAppointmentService {
    // 预约锁定时间：10分钟
    private static final long APPOINTMENT_LOCK_EXPIRE = 10;
    // 预约缓存过期时间：30分钟
    private static final long APPOINTMENT_CACHE_EXPIRE = 30;

    @Autowired
    private AppointmentMapper appointmentMapper;

    @Autowired
    private IAppointmentFeign appointmentFeign;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    /**
     * @Description: 查看部门
     * @Param:
     * @return:
     */
    @Override
    public RespondDTO queryDepartment() {
        //从redis里把数据掉出来
        Object departmentInfo = redisTemplate.opsForValue().get("departmentInfo");
        if (departmentInfo != null) {
            return RespondDTO.success(departmentInfo);
        }

        //如果redis里的数据为空的话，那就从数据库里面掉一份出来，在存到redis中，这样可以避免过度对数据库进行访问
        List<DepartmentDO> queryDepartment = appointmentFeign.queryDepartment();
        System.err.println(queryDepartment);
        //构建树形菜单
        List<DepartmentDO> treeMenu = TreeMenuUtils.buildTreeMenu(queryDepartment);
        redisTemplate.opsForValue().set("departmentInfo", treeMenu);
        if (treeMenu != null) {
            return RespondDTO.success(treeMenu);
        }
        return null;
    }

    /**
     * 查询医生列表
     */
    @Override
    public List<DoctorDO> queryDoctor(Integer subItemId) {
        return appointmentMapper.queryDoctor(subItemId);
    }

    /**
     * 查询医生的可预约时段
     */
    @Override
    public List<AppointmentDO> queryAppointment(Integer doctorId) {
        // 定义Redis键
        String redisKey = "doctor:" + doctorId + ":appointments";

        // 先从Redis查询
        Object cachedAppointments = redisTemplate.opsForValue().get(redisKey);
        if (cachedAppointments != null && cachedAppointments instanceof List) {
            //
            System.out.println("预约信息是从redis中查出来的");
            return (List<AppointmentDO>) cachedAppointments;
        }

        // 缓存不存在，从数据库查询
        List<AppointmentDO> appointments = appointmentMapper.queryAppointment(doctorId);

        if (appointments != null) {
            // 存入Redis并设置过期时间
            redisTemplate.opsForValue().set(redisKey, appointments,
                    APPOINTMENT_CACHE_EXPIRE, TimeUnit.MINUTES);
        }
        return appointments;
    }

    /**
     * 创建预约（锁定时段） 分布式锁	appointment:lock:300 过期时间为10秒
     */
    @Transactional
    @Override
    public RespondDTO createAppointment(Integer appointmentId, Integer userId) {
        // 1. 检查该预约是否可预约
        AppointmentDO appointment = appointmentMapper.queryById(appointmentId);
        if (appointment == null || appointment.getAppointmentStatus() != 0) {
            return RespondDTO.fail("该时段不可预约");
        }

        // 2. 使用Redis实现分布式锁，防止并发问题
        String lockKey = "appointment:lock:" + appointmentId;
        System.out.println("尝试获取锁：" + lockKey); // 输出要获取的锁键

        Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, userId, 10, TimeUnit.SECONDS);

        // 输出获取锁的结果
        if (locked == null) {
            System.out.println("Redis连接异常，获取锁无法获取锁");
        } else if (locked) {
            System.out.println("成功获取锁：" + lockKey);
        } else {
            System.out.println("获取锁失败（已被占用）：" + lockKey);
        }

        if (locked == null || !locked) {
            return RespondDTO.fail("操作太频繁，请稍后再试");
        }

        try {
            // 3. 更新数据库状态
            int rows = appointmentMapper.updateStatus(appointmentId, userId, 1); // 状态1表示已预约未支付
            if (rows <= 0) {
                return RespondDTO.fail("预约失败，请重试");
            }

            // 4. 更新Redis缓存（删除旧缓存，下次查询自动加载最新数据）
            String doctorAppointmentsKey = "doctor:" + appointment.getDocId() + ":appointments";
            redisTemplate.delete(doctorAppointmentsKey);

            // 5. 设置预约超时锁（10分钟）
            String expireKey = "appointment:expire:" + appointmentId;
            redisTemplate.opsForValue().set(expireKey, userId, APPOINTMENT_LOCK_EXPIRE, TimeUnit.MINUTES);

            return RespondDTO.success("预约成功，请在10分钟内完成支付", appointmentId);
        } finally {
            // 释放分布式锁
            redisTemplate.delete(lockKey);
        }
    }

    /**
     * 确认支付
     */
    @Transactional
    @Override
    public RespondDTO confirmPayment(Integer appointmentId, Integer userId) {
        // 1. 检查预约是否存在且属于当前用户
        AppointmentDO appointment = appointmentMapper.queryById(appointmentId);
        if (appointment == null || !userId.equals(appointment.getUserId()) || appointment.getAppointmentStatus() != 1) {
            return RespondDTO.fail("无效的预约信息");
        }

        // 2. 更新数据库状态为已支付
        int rows = appointmentMapper.updateStatus(appointmentId, userId, 2); // 状态2表示已支付
        if (rows <= 0) {
            return RespondDTO.fail("支付确认失败");
        }

        // 3. 清除超时锁
        String expireKey = "appointment:expire:" + appointmentId;
        redisTemplate.delete(expireKey);

        // 4. 更新缓存
        String doctorAppointmentsKey = "doctor:" + appointment.getDocId() + ":appointments";
        redisTemplate.delete(doctorAppointmentsKey);

        return RespondDTO.success("支付成功");
    }

    /**
     * 处理超时未支付的预约
     */
    public void handleExpiredAppointment(Integer appointmentId) {
        // 1. 查询预约信息
        AppointmentDO appointment = appointmentMapper.queryById(appointmentId);
        if (appointment == null || appointment.getAppointmentStatus() != 1) {
            return; // 状态不是未支付，无需处理
        }

        // 2. 释放预约（状态改回0，清空userId）
        appointmentMapper.releaseAppointment(appointmentId);

        // 3. 更新缓存
        String doctorAppointmentsKey = "doctor:" + appointment.getDocId() + ":appointments";
        redisTemplate.delete(doctorAppointmentsKey);
    }
}
